/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2025 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TGUI_TREE_VIEW_HPP
#define TGUI_TREE_VIEW_HPP

#include <TGUI/Widgets/Scrollbar.hpp>
#include <TGUI/Renderers/TreeViewRenderer.hpp>
#include <TGUI/Text.hpp>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief Tree view widget
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API TreeView : public Widget, public DualScrollbarChildInterface
    {
    public:

        using Ptr = std::shared_ptr<TreeView>;
        using ConstPtr = std::shared_ptr<const TreeView>;

        static constexpr const char StaticWidgetType[] = "TreeView"; //!< Type name of the widget

        /// @brief Read-only node representation used by getNode and getNodes
        struct ConstNode
        {
            bool expanded = true;
            String text;
            std::vector<ConstNode> nodes;
        };

        /// @brief Internal representation of a node
        struct Node
        {
            Text text;
            unsigned int depth = 0;
            bool expanded = true;
            Node* parent = nullptr;
            std::vector<std::shared_ptr<Node>> nodes;
        };

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// @brief Constructor
        /// @param typeName     Type of the widget
        /// @param initRenderer Should the renderer be initialized? Should be true unless a derived class initializes it.
        /// @see create
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TreeView(const char* typeName = StaticWidgetType, bool initRenderer = true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TreeView(const TreeView&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Move constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TreeView(TreeView&&) = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Overload of copy assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TreeView& operator=(const TreeView&);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Overload of move assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TreeView& operator=(TreeView&&) = default;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Creates a new tree view widget
        /// @return The new tree view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static TreeView::Ptr create();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Makes a copy of another tree view
        /// @param treeView  The other tree view
        /// @return The new tree view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static TreeView::Ptr copy(const TreeView::ConstPtr& treeView);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer that may be shared with other widgets using the same renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD TreeViewRenderer* getSharedRenderer() override;
        TGUI_NODISCARD const TreeViewRenderer* getSharedRenderer() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer
        /// @warning After calling this function, the widget has its own copy of the renderer and it will no longer be shared.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD TreeViewRenderer* getRenderer() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the size of the tree view
        /// @param size  The new size of the tree view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSize(const Layout2d& size) override;
        using Widget::setSize;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Adds a new item to the tree view
        ///
        /// @param hierarchy     Hierarchy of items, with the last item being the leaf item
        /// @param createParents Should the hierarchy be created if it did not exist yet?
        ///
        /// @return True when the item was added (always the case if createParents is true)
        ///
        /// Example code:
        /// @code
        /// treeView->addItem({"Smilies", "Happy"});
        /// treeView->addItem({"Smilies", "Sad"});
        /// treeView->addItem({"Smilies", "Neither"});
        /// treeView->addItem({"Vehicles", "Parts", "Wheel"});
        /// treeView->addItem({"Vehicles", "Whole", "Truck"});
        /// treeView->addItem({"Vehicles", "Whole", "Car"});
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool addItem(const std::vector<String>& hierarchy, bool createParents = true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text of a leaf item
        ///
        /// @param hierarchy  Hierarchy of items, with the last item being the leaf item that should be renamed
        /// @param leafText   New text to put in the leaf item
        ///
        /// @return True when the item was renamed, false if no item matched with the given hierarchy
        ///
        /// Example code:
        /// @code
        /// treeView->changeItem({"Vehicles", "Whole", "Car"}, "Train");  // Changes the item to {"Vehicles", "Whole", "Train"}
        /// @endcode
        ///
        /// @since TGUI 1.2
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool changeItem(const std::vector<String>& hierarchy, const String& leafText);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Expands the given item
        ///
        /// @param hierarchy  Hierarchy of items, identifying the node that has to be expanded
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void expand(const std::vector<String>& hierarchy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Expands all items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void expandAll();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Collapses the given item
        ///
        /// @param hierarchy  Hierarchy of items, identifying the node that has to be collapsed
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void collapse(const std::vector<String>& hierarchy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Collapses all items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void collapseAll();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Selects an item in the tree view
        ///
        /// @param hierarchy  Hierarchy of items, with the last item being the leaf item, which is being selected
        ///
        /// @return True when the item was selected, false when hierarchy was incorrect
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool selectItem(const std::vector<String>& hierarchy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Deselect the item if one was selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void deselectItem();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Removes an item
        ///
        /// @param hierarchy  Hierarchy of items, identifying the node to be removed
        /// @param removeParentsWhenEmpty  Also delete the parent of the deleted item if it has no other children
        ///
        /// @return True when the item existed and was removed, false when hierarchy was incorrect
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool removeItem(const std::vector<String>& hierarchy, bool removeParentsWhenEmpty = true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Removes all items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void removeAllItems();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the selected item
        /// @return Hierarchy of items, identifying the selected node, or an empty list when no item was selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<String> getSelectedItem() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the item currently below the mouse cursor
        /// @return Hierarchy of items, identifying the node below the mouse, or an empty list when the mouse isn't on any item
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<String> getHoveredItem() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets the index of an item, based on the items that share the same parent
        ///
        /// @param hierarchy  Hierarchy of items, with the last element being the leaf item
        /// @param index      The index that the item should have, when only looking at items where only the last element of the
        ///                   hierarchy is different. Passing a number that is too high will just move the item to the back,
        ///                   as if you passed to maximum valid number. Passing 0 moves the item to the front.
        ///
        /// @return True if the hierarchy matched an item in the tree view, false otherwise.
        ///
        /// @see getItemIndexInParent
        ///
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool setItemIndexInParent(const std::vector<String>& hierarchy, std::size_t index);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the index of an item, based on the items that share the same parent
        ///
        /// @param hierarchy  Hierarchy of items, with the last element being the leaf item
        ///
        /// @return The index of the item identified by the hierarchy, when only looking at items where only the last element
        ///         of the hierarchy is different. Returns -1 if the hierarchy parameter didn't match an item in the tree view.
        ///
        /// In the following example, both A, A1 and B1 would have index 0, while B and A2 would have index 1.
        /// @code
        /// A
        ///   A1
        ///   A2
        /// B
        ///   B1
        /// @endcode
        ///
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        int getItemIndexInParent(const std::vector<String>& hierarchy) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Moves one item and its children from one location in the hierarchy to a completely different location
        ///
        /// @param oldHierarchy  The item that needs to be moved
        /// @param newHierarchy  The new location of the item
        ///
        /// @return True if the node was moved, false if oldHierarchy wasn't found or newHierarchy without the last element wasn't found
        ///
        /// Example. Suppose you have the following tree:
        /// @code
        /// A
        ///   A1
        ///     A1_1
        ///     A1_2
        ///   A2
        /// B
        ///   B1
        /// @endcode
        /// Calling changeItemHierarchy({"A", "A1"}, {"B", "X"}) will result in the following tree:
        /// @code
        /// A
        ///   A2
        /// B
        ///   B1
        ///   X
        ///     A1_1
        ///     A1_2
        /// @endcode
        ///
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool changeItemHierarchy(const std::vector<String>& oldHierarchy, const std::vector<String>& newHierarchy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the node in the tree view at a given hierarchy
        ///
        /// @param hierarchy  Hierarchy of items, identifying the node to retrieve
        ///
        /// @return Node that was identified by the hierarchy
        ///
        /// If no node exists at the given hierarchy, the returned node will have an empty text and no children.
        /// If the node was found, its text property will always match with the last element of the hierarchy.
        ///
        /// @since TGUI 1.2
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ConstNode getNode(const std::vector<String>& hierarchy) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the nodes in the tree view
        /// @return List of nodes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<ConstNode> getNodes() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the height of the items in the tree view
        /// @param itemHeight  The size of a single item in the tree view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setItemHeight(unsigned int itemHeight);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of the items in the tree view
        /// @return The item height
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getItemHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the thumb position of the vertical scrollbar
        ///
        /// @param value  New value of the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->setValue(value) instead") void setVerticalScrollbarValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the thumb position of the vertical scrollbar
        ///
        /// @return Value of the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->getValue() instead") TGUI_NODISCARD unsigned int getVerticalScrollbarValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum thumb position of the vertical scrollbar
        ///
        /// @return Maximum value of the vertical scrollbar
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->getMaxValue() instead") TGUI_NODISCARD unsigned int getVerticalScrollbarMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the thumb position of the horizontal scrollbar
        ///
        /// @param value  New value of the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->setValue(value) instead") void setHorizontalScrollbarValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the thumb position of the horizontal scrollbar
        ///
        /// @return Value of the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->getValue() instead") TGUI_NODISCARD unsigned int getHorizontalScrollbarValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum thumb position of the horizontal scrollbar
        ///
        /// @return Maximum value of the horizontal scrollbar
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->getMaxValue() instead") TGUI_NODISCARD unsigned int getHorizontalScrollbarMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the mouse position (which is relative to the parent widget) lies on top of the widget
        ///
        /// @return Is the mouse on top of the widget?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isMouseOnWidget(Vector2f pos) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool leftMousePressed(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseReleased(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rightMousePressed(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseMoved(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool scrolled(float delta, Vector2f pos, bool touch) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseNoLongerOnWidget() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseButtonNoLongerDown() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void keyPressed(const Event::KeyEvent& event) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called by the parent of the widget to check if keyPressed would process the event
        ///
        /// @param event  Key event that took place
        ///
        /// @return True if the event would be handled by the widget, false if the key event doesn't affect the widget
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool canHandleKeyPress(const Event::KeyEvent& event) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void markNodesDirty();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Draw the widget to a render target
        ///
        /// @param target Render target to draw to
        /// @param states Current render states
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void draw(BackendRenderTarget& target, RenderStates states) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves a signal based on its name
        ///
        /// @param signalName  Name of the signal
        ///
        /// @return Signal that corresponds to the name
        ///
        /// @throw Exception when the name does not match any signal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Signal& getSignal(String signalName) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Function called when one of the properties of the renderer is changed
        ///
        /// @param property  Name of the property that was changed
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rendererChanged(const String& property) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Saves the widget as a tree node in order to save it to a file
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::unique_ptr<DataIO::Node> save(SavingRenderersMap& renderers) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Loads the widget from a tree of nodes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void load(const std::unique_ptr<DataIO::Node>& node, const LoadingRenderersMap& renderers) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the text size is changed (either by setTextSize or via the renderer)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateTextSize() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the policy of one of the scrollbars has been changed calling either
        ///        getVerticalScrollbar()->setPolicy(...) or getHorizontalScrollbar()->setPolicy(...)
        ///
        /// @param orientation  Vertical or Horizontal depending on which scrollbar triggered the callback
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void scrollbarPolicyChanged(Orientation orientation) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns the size without the borders
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f getInnerSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the bounds of the icons
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateIconBounds();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function is called every frame with the time passed since the last frame.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateTime(Duration elapsedTime) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Makes a copy of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Widget::Ptr clone() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateTextColors(std::vector<std::shared_ptr<Node>>& nodes);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void createNode(std::vector<std::shared_ptr<Node>>& menus, Node* parent, const String& text);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Node* findParentNode(const std::vector<String>& hierarchy, unsigned int parentIndex, std::vector<std::shared_ptr<Node>>& nodes, Node* parent, bool createParents);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Expands or collapses one of the visible items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void toggleNodeInternal(std::size_t index);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Expands or collapses a node
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool expandOrCollapse(const std::vector<String>& hierarchy, bool expand);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Helper function to load the items from a text file
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void loadItems(const std::unique_ptr<DataIO::Node>& node, std::vector<std::shared_ptr<Node>>& items, Node* parent);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Rebuilds the list of visible items and positions the texts
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        unsigned int updateVisibleNodes(std::vector<std::shared_ptr<Node>>& nodes, Node* selectedNode, float textPadding, unsigned int pos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the text colors of the selected and hovered items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSelectedAndHoveringItemColors();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the text color of the hovered item
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateHoveredItem(int item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates the text color of the selected item
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSelectedItem(int item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        SignalItemHierarchy onItemSelect = {"ItemSelected"};   //!< An node was selected in the tree view. Optional parameter: selected node
        SignalItemHierarchy onDoubleClick = {"DoubleClicked"}; //!< A leaf node was double clicked. Optional parameter: selected node
        SignalItemHierarchy onExpand = {"Expanded"};           //!< A branch node was expanded in the tree view. Optional parameter: expanded node
        SignalItemHierarchy onCollapse = {"Collapsed"};        //!< A branch node was collapsed in the tree view. Optional parameter: collapsed node
        SignalItemHierarchy onRightClick = {"RightClicked"};   //!< Right mouse button was pressed on top of a node. Optional parameter: node below mouse

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        // This contains the nodes of the tree
        std::vector<std::shared_ptr<Node>> m_nodes;
        std::vector<std::shared_ptr<Node>> m_visibleNodes;

        int m_selectedItem = -1;
        int m_hoveredItem = -1;

        unsigned int m_itemHeight = 0;
        float m_maxRight = 0;

        Vector2f m_iconBounds;

        bool m_possibleDoubleClick = false;
        int m_doubleClickNodeIndex = -1;

        Sprite    m_spriteBackground;
        Sprite    m_spriteBranchExpanded;
        Sprite    m_spriteBranchCollapsed;
        Sprite    m_spriteLeaf;

        // Cached renderer properties
        Borders   m_bordersCached;
        Borders   m_paddingCached;
        Color     m_borderColorCached;
        Color     m_backgroundColorCached;
        Color     m_textColorCached;
        Color     m_textColorHoverCached;
        Color     m_selectedTextColorCached;
        Color     m_selectedTextColorHoverCached;
        Color     m_selectedBackgroundColorCached;
        Color     m_selectedBackgroundColorHoverCached;
        Color     m_backgroundColorHoverCached;
        TextStyles m_textStyleCached;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_TREE_VIEW_HPP
